#ifndef GLOBAL_VALUE_H
#define GLOBAL_VALUE_H

#include "utils/cxx-include.hpp"

#include "core/exception.hpp"
#include "core/log.hpp"

/**
 * \file
 * \ingroup globalValue
 * cas::GlobalValue declaration.
 */

/**
 * \ingroup core
 * \defgroup globalValue Global Value
 */

namespace cas
{
  /**
   * \ingroup globalValue
   * \brief Hold a so-called 'global value'.
   *
   * A GlobalValue will get its value from (in order):
   *   - The initial value configured where it is defined,
   *   - From the \c CAS_GLOBAL_VALUE environment variable,
   *   - From the command line,
   *   - By explicit call to SetValue() or Bind().
   *
   * Instances of this class are expected to be allocated as static
   * global variables and should be used to store configurable global state.
   * For example:
   * \code
   *   static GlobalValue g_myGlobal ("myGlobal", "My global value for ...",
   *       IntegerValue (12), MakeIntegerChecker ());
   * \endcode
   *
   * GlobalValues can be set directly by calling GlobalValue::SetValue()
   * but they can also be set through the \c CAS_GLOBAL_VALUE environment variable.
   * For example, \c CAS_GLOBAL_VALUE='Name1=Value1;Name2=Other2;' would set
   * global values \c Name1 and \c Name2 to \c Value1 and \c Value2,
   * respectively.
   *
   * Users of the CommandLine class also get the ability to set global
   * values through command line arguments to their program:
   * \c --Name=Value will set global value \c Name to \c Value.
   */
  class GlobalValue
  {
    /** Container type for holding all the GlobalValues. */
    typedef std::vector<GlobalValue *> Vector;

  public:
    /** Iterator type for the list of all global values. */
    typedef Vector::const_iterator Iterator;

    /**
     * Constructor.
     * \param [in] name the name of this global value.
     * \param [in] help some help text which describes the purpose of this
     *        global value.
     * \param [in] initialValue the value to assign to this global value
     *        during construction.
     */
    GlobalValue(std::string name, std::string help, const std::string &initialValue);

    /**
     * Get the name.
     * \returns The name of this GlobalValue.
     */
    std::string GetName(void) const;

    /**
     * Get the help string.
     * \returns The help text of this GlobalValue.
     */

    std::string GetHelp(void) const;
    /**
     * Get the value.
     * \returns The current value of this GlobalValue.
     */
    std::string GetValue() const;

    /**
     * Set the value of this GlobalValue.
     * \param [in] value the new value to set in this GlobalValue.
     * \returns \c true if the Global Value was set successfully.
     */
    bool SetValue(const std::string &value);

    /** Reset to the initial value. */
    void ResetValue(void);

    /**
     * Iterate over the set of GlobalValues until a matching name is found
     * and then set its value with GlobalValue::SetValue.
     *
     * \param [in] name the name of the global value
     * \param [in] value the value to set in the requested global value.
     *
     * This method cannot fail. It will crash if the input is not valid.
     */
    static void Bind(std::string name, const std::string &value);

    /**
     * Iterate over the set of GlobalValues until a matching name is found
     * and then set its value with GlobalValue::SetValue.
     *
     * \param [in] name the name of the global value
     * \param [in] value the value to set in the requested global value.
     * \returns \c true if the value could be set successfully,
     *          \c false otherwise.
     */
    static bool BindFailSafe(std::string name, const std::string &value);

    /**
     * The Begin iterator.
     * \returns An iterator which represents a pointer to the first GlobalValue registered.
     */
    static Iterator Begin(void);
    /**
     * The End iterator.
     * \returns An iterator which represents a pointer to the last GlobalValue registered.
     */
    static Iterator End(void);

    /**
     * Finds the GlobalValue with the given name and returns its value
     *
     * \param [in] name the name of the GlobalValue to be found
     * \param [out] value where to store the value of the found GlobalValue
     *
     * \return \c true if the GlobalValue was found, \c false otherwise
     */
    static bool GetValueByNameFailSafe(std::string name, std::string &value);

    /**
     * Finds the GlobalValue with the given name and returns its
     * value.
     *
     * This method cannot fail, i.e., it will trigger a
     * NS_FATAL_ERROR if the requested GlobalValue is not found.
     *
     * \param [in] name the name of the GlobalValue to be found
     * \param [out] value where to store the value of the found GlobalValue
     */
    static void GetValueByName(std::string name, std::string &value);

  private:
    /**
     * Get the static vector of all GlobalValues.
     *
     * \returns The vector.
     */
    static Vector *GetVector(void);
    /** Initialize from the \c NS_GLOBAL_VALUE environment variable. */
    void InitializeFromEnv(void);

    /** The name of this GlobalValue. */
    std::string m_name;
    /** The help string. */
    std::string m_help;
    /** The initial value. */
    std::string m_initialValue;
    /** The current value. */
    std::string m_currentValue;
  };

} // namespace cas

#endif /* GLOBAL_VALUE_H */
